
package com.jf.cloud.product.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.jf.cloud.api.auth.constant.SysTypeEnum;
import com.jf.cloud.api.auth.feign.AccountFeignClient;
import com.jf.cloud.api.auth.vo.AuthAccountVO;
import com.jf.cloud.common.database.dto.PageDTO;
import com.jf.cloud.common.database.util.PageUtil;
import com.jf.cloud.common.database.vo.PageVO;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.i18n.I18nMessage;
import com.jf.cloud.common.product.constant.StockBillType;
import com.jf.cloud.common.product.constant.TakeStockBillStatus;
import com.jf.cloud.common.product.constant.TakeStockExceptionEnum;
import com.jf.cloud.common.product.constant.TakeStockSpuStatus;
import com.jf.cloud.common.product.dto.SkuDTO;
import com.jf.cloud.common.util.ExcelUtil;
import com.jf.cloud.product.dto.TakeStockDTO;
import com.jf.cloud.product.mapper.TakeStockMapper;
import com.jf.cloud.product.model.StockBillLog;
import com.jf.cloud.product.model.TakeStock;
import com.jf.cloud.product.model.TakeStockSpu;
import com.jf.cloud.product.service.*;
import com.jf.cloud.product.vo.TakeStockExcelVO;
import com.jf.cloud.product.vo.TakeStockVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 *
 * @author zz
 * @date 2022-06-22 16:57:27
 */
@Service
public class TakeStockServiceImpl implements TakeStockService {

    @Autowired
    private TakeStockMapper takeStockMapper;
    @Autowired
    private TakeStockSpuService takeStockSpuService;
    @Autowired
    private SpuService spuService;
    @Autowired
    private SkuService skuService;
    @Autowired
    private StockBillLogService stockBillLogService;
    @Autowired
    private AccountFeignClient accountFeignClient;

    @Value("${jfcloud.expose.operation.auth:}")
    private Boolean permission;

    @Override
    public PageVO<TakeStock> page(PageDTO pageDTO) {
        return PageUtil.doPage(pageDTO, () -> takeStockMapper.list());
    }

    @Override
    public TakeStock getByTakeStockId(Long takeStockId) {
        return takeStockMapper.getByTakeStockId(takeStockId);
    }

    @Override
    public void save(TakeStock takeStock) {
        takeStockMapper.save(takeStock);
    }

    @Override
    public void update(TakeStock takeStock) {
        takeStockMapper.update(takeStock);
    }

    @Override
    public void deleteById(Long takeStockId) {
        takeStockMapper.deleteById(takeStockId);
    }

    @Override
    public PageVO<TakeStockVO> pageByParam(PageDTO pageDTO, TakeStockDTO takeStockDTO) {
        PageVO<TakeStockVO> page = PageUtil.doPage(pageDTO, () -> takeStockMapper.pageByParam(I18nMessage.getLang(), takeStockDTO));
        // 数据准备
        List<TakeStockVO> takeStockVOS = page.getList();
        Set<Long> userIds = takeStockVOS.stream().map(TakeStockVO::getMaker).collect(Collectors.toSet());
        List<AuthAccountVO> accounts = accountFeignClient.listByUserIdsAndPhoneAndType(new ArrayList<>(userIds), null, SysTypeEnum.MULTISHOP.value()).getData();
        Map<Long, String> userIdNameMap = new HashMap<>(16);
        if (!CollectionUtils.isEmpty(accounts)) {
            userIdNameMap = accounts.stream().collect(Collectors.toMap(AuthAccountVO::getUserId, AuthAccountVO::getUsername));;
        }
        // 信息补充or处理
        for (TakeStockVO takeStockVO : takeStockVOS) {
            // 补充制单人名称
            takeStockVO.setMakerName(userIdNameMap.get(takeStockVO.getMaker()));
            // 手机号码处理
            if (BooleanUtil.isFalse(permission) && StrUtil.isNotBlank(takeStockVO.getMakerMobile())){
                takeStockVO.setMakerMobile(PhoneUtil.hideBetween(takeStockVO.getMakerMobile()).toString());
            }
        }
        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean updateTakeStock(TakeStockDTO takeStockDTO) {
        // 校验实物盘点信息
        this.checkInfo(takeStockDTO, false);
        // 更新实物盘点
        TakeStock takeStock = new TakeStock();
        BeanUtils.copyProperties(takeStockDTO, takeStock);
        takeStockMapper.update(takeStock);
        // 补充商品盘盈盘亏状态
        if (!CollectionUtils.isEmpty(takeStockDTO.getTakeStockSpuList())) {
            for (TakeStockSpu takeStockSpu : takeStockDTO.getTakeStockSpuList()) {
                if (takeStockSpu.getStocks().compareTo(takeStockSpu.getTotalStock()) > 0) {
                    takeStockSpu.setIoType(TakeStockSpuStatus.LOSS.value());
                } else if (takeStockSpu.getStocks().compareTo(takeStockSpu.getTotalStock()) < 0) {
                    takeStockSpu.setIoType(TakeStockSpuStatus.PROFIT.value());
                } else {
                    takeStockSpu.setIoType(TakeStockSpuStatus.EQUAL.value());
                }
            }
        }
        // 更新实物盘点商品
        takeStockSpuService.updateByTakeStockDTO(takeStockDTO);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean finishTakeStock(TakeStockDTO takeStockDTO) {
        // 数据校验
        this.checkInfo(takeStockDTO, false);
        // 更新实物盘点
        TakeStock takeStock = new TakeStock();
        BeanUtils.copyProperties(takeStockDTO, takeStock);
        takeStock.setBillStatus(TakeStockBillStatus.COMPLETED.value());
        takeStockMapper.update(takeStock);
        // 更新库存并获取ioType分类
        if (CollectionUtils.isEmpty(takeStockDTO.getTakeStockSpuList())) {
            return true;
        }
        Map<Integer, List<SkuDTO>> ioTypeSkuMap  = this.updateSkuAndGetIoTypeMap(takeStockDTO.getTakeStockSpuList());
        // 更新实物盘点商品
        takeStockSpuService.updateByTakeStockDTO(takeStockDTO);
        // 记录出入库信息
        // 盘盈记录
        StockBillLog profitStockBillLog = new StockBillLog();
        profitStockBillLog.setShopId(takeStock.getShopId());
        profitStockBillLog.setEmployeeId(takeStock.getMaker());
        profitStockBillLog.setMakerMobile(takeStock.getMakerMobile());
        profitStockBillLog.setStockBillType(StockBillType.PROFIT_STORAGE.value());
        // 来源单号为long类型，只能把盘点单号前面的TSC去掉
        profitStockBillLog.setSourceOrderNo(Long.parseLong(takeStock.getTakeStockNo().substring(3)));
        stockBillLogService.generateStockLogOfTakeStock(profitStockBillLog, ioTypeSkuMap.get(TakeStockSpuStatus.PROFIT.value()));
        // 盘亏记录
        StockBillLog lossStockBillLog = new StockBillLog();
        lossStockBillLog.setShopId(takeStock.getShopId());
        lossStockBillLog.setEmployeeId(takeStock.getMaker());
        lossStockBillLog.setMakerMobile(takeStock.getMakerMobile());
        lossStockBillLog.setStockBillType(StockBillType.LOSS_OUTBOUND.value());
        lossStockBillLog.setSourceOrderNo(Long.parseLong(takeStock.getTakeStockNo().substring(3)));
        stockBillLogService.generateStockLogOfTakeStock(lossStockBillLog, ioTypeSkuMap.get(TakeStockSpuStatus.LOSS.value()));
        return true;
    }

    @Override
    public void exportExcel(TakeStockDTO takeStockDTO, HttpServletResponse response) {
        // 获取excel导出数据
        List<TakeStockExcelVO> takeStockExcelVOList = takeStockMapper.listTakeStockExportData(takeStockDTO, I18nMessage.getLang());
        // 制单人名称数据准备
        Set<Long> userIds = takeStockExcelVOList.stream().map(TakeStockExcelVO::getMaker).collect(Collectors.toSet());
        List<AuthAccountVO> accounts = accountFeignClient.listByUserIdsAndPhoneAndType(new ArrayList<>(userIds), null, SysTypeEnum.MULTISHOP.value()).getData();
        Map<Long, String> userIdNameMap = new HashMap<>(16);
        if (!CollectionUtils.isEmpty(accounts)) {
            userIdNameMap = accounts.stream().collect(Collectors.toMap(AuthAccountVO::getUserId, AuthAccountVO::getUsername));;
        }
        // 数据转换
        TakeStockBillStatus billStatusEnum;
        TakeStockSpuStatus spuStatusEnum;
        for (TakeStockExcelVO takeStockExcelVO : takeStockExcelVOList) {
            // 盘点状态
            billStatusEnum = TakeStockBillStatus.instance(takeStockExcelVO.getBillStatus());
            takeStockExcelVO.setBillStatusStr(billStatusEnum == null ? "" : billStatusEnum.strCn());
            // 盈亏状态
            spuStatusEnum = TakeStockSpuStatus.instance(takeStockExcelVO.getIoType());
            takeStockExcelVO.setIoTypeStr(spuStatusEnum == null ? "" : spuStatusEnum.str());
            // 制单人名称
            takeStockExcelVO.setMakerName(userIdNameMap.get(takeStockExcelVO.getMaker()));
        }
        // 数据导出为excel
        ExcelWriter excelWriter = null;
        try {
            // 先执行合并策略
            ExcelWriterBuilder writerBuilder = ExcelUtil.getExcelWriterMerge(response, TakeStockExcelVO.EXCEL_NAME, TakeStockExcelVO.MERGE_ROW_INDEX, TakeStockExcelVO.MERGE_COLUMN_INDEX);
            if (CollUtil.isNotEmpty(takeStockExcelVOList)){
                excelWriter = writerBuilder.build();
                // 进行写入操作
                WriteSheet sheetWriter = EasyExcel
                        .writerSheet(TakeStockExcelVO.SHEET_NAME)
                        .head(TakeStockExcelVO.class)
                        .build();
                excelWriter.write(takeStockExcelVOList, sheetWriter);
            }
        } catch (Exception e) {
            System.out.println("导出excel失败: " + e.getMessage());
        }finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    /**
     * 校验实物盘点信息
      * @param takeStock
     * @param isVoid
     */
    private void checkInfo(TakeStockDTO takeStock, boolean isVoid) {
        TakeStock dbTakeStock = takeStockMapper.getByTakeStockId(takeStock.getTakeStockId());
        if (!Objects.equals(dbTakeStock.getShopId(), takeStock.getShopId())) {
            throw new LuckException("当前盘点信息不属于你的店铺");
        }
        if (Objects.equals(dbTakeStock.getBillStatus(), TakeStockBillStatus.COMPLETED.value())
                || Objects.equals(dbTakeStock.getBillStatus(), TakeStockBillStatus.VOIDED.value())) {
            throw new LuckException(("当前盘点任务已完成或已作废，请刷新页面"));
        }
        //不是废弃再进行判断
        if (!isVoid) {
            List<TakeStockSpu> prods = takeStock.getTakeStockSpuList();
            for (TakeStockSpu prod : prods) {
                if (Objects.isNull(prod.getTotalStock())) {
                    throw new LuckException("商品实盘库存不能为空");
                }
            }
        }
    }

    /**
     * 更新库存并获取分类ioTypeMap
     * @param takeStockProdList
     * @return
     */
    private Map<Integer, List<SkuDTO>> updateSkuAndGetIoTypeMap(List<TakeStockSpu> takeStockProdList) {
        List<Long> skuIds = takeStockProdList.stream().map(TakeStockSpu::getSkuId).collect(Collectors.toList());
        Map<Long, SkuDTO> skuIdSkuMap = skuService.listSkuDtoBySkuIds(skuIds).stream().collect(Collectors.toMap(SkuDTO::getSkuId, s -> s));
        // 根据sku库存盘点判断盘盈or盘亏，并进行归类
        Map<Integer, List<SkuDTO>> ioTypeSkuMap = new HashMap<>(16);
        List<SkuDTO> skuDtoList;
        Integer ioType;
        SkuDTO sku;
        for (TakeStockSpu prod : takeStockProdList) {
            sku = skuIdSkuMap.get(prod.getSkuId());
            //盈亏以及异常判断
            if (Objects.isNull(sku)) {
                //sku删除，异常
                prod.setIoType(TakeStockSpuStatus.EXCEPTION.value());
                prod.setExceptionReason(TakeStockExceptionEnum.DELETE_PROD.value());
                continue;
            } else if (!prod.getStocks().equals(sku.getStock())) {
                //库存不相等，异常
                prod.setIoType(TakeStockSpuStatus.EXCEPTION.value());
                prod.setExceptionReason(TakeStockExceptionEnum.PROD_CHANGE.value());
                continue;
            }
            if (prod.getStocks() > prod.getTotalStock()) {
                //盘亏
                prod.setIoType(TakeStockSpuStatus.LOSS.value());
                ioType = TakeStockSpuStatus.LOSS.value();
            } else if (prod.getStocks() < prod.getTotalStock()) {
                //盘盈
                prod.setIoType(TakeStockSpuStatus.PROFIT.value());
                ioType = TakeStockSpuStatus.PROFIT.value();
            } else {
                //盘平
                prod.setIoType(TakeStockSpuStatus.EQUAL.value());
                continue;
            }
            // 分类
            sku.setChangeStock(prod.getTotalStock() - prod.getStocks());
            sku.setStock(prod.getTotalStock());
            skuDtoList = ioTypeSkuMap.getOrDefault(ioType, new ArrayList<>());
            skuDtoList.add(sku);
            ioTypeSkuMap.put(ioType, skuDtoList);
            // 更新库存
            spuService.updateSpuAndSkuStock(prod.getSpuId(), prod.getSkuId(), sku.getChangeStock());
        }
        return ioTypeSkuMap;
    }
}
